package util

import (
	"context"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/chromedp/cdproto/cdp"
	"github.com/chromedp/cdproto/network"
	"github.com/chromedp/chromedp"
	mapset "github.com/deckarep/golang-set"
	"log"
	"mp4/entity"
	"os"
	"regexp"
	"strconv"
	"strings"
	"time"
)

func ChoosePlayList(configEntity entity.DownloadEntity, flag bool) (*strings.Reader, []entity.PlayListEntity, entity.ResponseEntity) {
	var responseEntity entity.ResponseEntity
	var html *strings.Reader
	var plaList []entity.PlayListEntity
	if flag {
		responseEntity, _, html = DownPage(configEntity.URL, entity.DownloadEntity{}, false)

		for i := 0; i < 5; i++ {
			plaList, flag = playList(responseEntity.Document, configEntity)
			plaList = sortPlayList(plaList)
			if len(plaList) == 0 && flag && IsNotEmpty(responseEntity.BodyStr) {
				log.Println("playList is nil", configEntity.FileName, "bodyStrLength", len(responseEntity.BodyStr))
				time.Sleep(Default_Sleep_value)
				responseEntity, _, html = DownPage(configEntity.URL, entity.DownloadEntity{}, false)
				continue
			}
		}
	}

	lenth := len(plaList)
	var plaLists []entity.PlayListEntity
	if lenth > 0 {
		for i := 0; i < lenth; i++ {
			re := plaList[i]
			path := configEntity.FileDirPath + "/" + re.TitleNames[0] + configEntity.AudioType
			var paths []string
			paths = append(paths, path)
			//flag, _ := ValidateFile(path, configEntity, paths)
			//if flag && configEntity.StartIndex > re.Index {
			//	configEntity.StartIndex = re.Index
			//} else if !flag && re.Index > configEntity.StartIndex {
			//	plaLists = append(plaLists, re)
			//}

			if configEntity.StartIndex > re.Index {
				configEntity.StartIndex = re.Index
			} else if re.Index > configEntity.StartIndex {
				plaLists = append(plaLists, re)
			}
		}
	}

	log.Println("play list over--->", len(plaLists))
	return html, plaLists, responseEntity
}

/*
下载页面并转换为goquery对象
*/
func DownPage(url string, config entity.DownloadEntity, bool2 bool) (entity.ResponseEntity, []*cdp.Node, *strings.Reader) {

	options := []chromedp.ExecAllocatorOption{
		//chromedp.Flag("blink-settings", "imagesEnabled=false"),
		//chromedp.Flag("no-default-browser-check", true),
		//chromedp.Flag("headless", true),
		//chromedp.Flag("hide-scrollbars", false),
		//chromedp.Flag("mute-audio", false),

		chromedp.UserAgent(`Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36`),
	}
	options = append(chromedp.DefaultExecAllocatorOptions[:], options...)
	//创建chrome窗口
	allocCtx, cancel := chromedp.NewExecAllocator(context.Background(), options...)
	defer cancel()

	ctx, cancel := chromedp.NewContext(allocCtx)
	chromedp.WSURLReadTimeout(Default_Sleep_value)
	chromedp.WithDialTimeout(Default_Sleep_value)
	chromedp.WithPollingTimeout(Default_Sleep_value)

	heads := make(map[string]string)

	var bodyStr string
	var iframeStr string
	var nodes []*cdp.Node

	bodyStr, nodes = switchChromedp(ctx, url, heads, config)

	html := strings.NewReader(bodyStr)
	body, _ := goquery.NewDocumentFromReader(strings.NewReader(bodyStr))
	iframeDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(iframeStr))

	return entity.ResponseEntity{Document: body, IFrameDocument: iframeDoc, Heads: heads, BodyStr: bodyStr}, nodes, html
}

func switchChromedp(ctx context.Context, url string, cookieMap map[string]string, config entity.DownloadEntity) (string, []*cdp.Node) {
	var bodyStr string
	var nodes []*cdp.Node

	//var val string

	err := chromedp.Run(ctx,
		chromedp.Navigate(url),
		chromedp.Sleep(time.Duration(config.ChromedpSleep)*time.Second),
		chromedp.OuterHTML(`body`, &bodyStr, chromedp.ByQuery),
		chromedp.ActionFunc(func(ctx context.Context) error {

			cookes, _ := network.GetCookies().Do(ctx)
			for _, v := range cookes {
				cookieMap[v.Name] = v.Value
			}
			return nil
		}),
	)
	if nil != err {
		log.Println("chrome error", err)
	}

	return bodyStr, nodes
}

/**获取列表*/
func playList(body *goquery.Document, configEntity entity.DownloadEntity) ([]entity.PlayListEntity, bool) {
	var entities []entity.PlayListEntity
	flag := true
	index2 := -1
	if IsEmpty(configEntity.PlayListTag) {
		body.Find(configEntity.PlayListChildTag).Each(func(index1 int, node *goquery.Selection) {
			if !SwitchHtmlElement(node, configEntity.PlayListChildURLAttName) {
				return
			}
			flag = false
			var src, _ = node.Attr(configEntity.PlayListChildURLAtt)
			src = chooseURL(src, configEntity.BaseURL)
			index2++

			title := chooseTitle(configEntity, node, index2+1)
			title = strings.ReplaceAll(title, "第", "")
			title = strings.ReplaceAll(title, "集", "")
			TitleStr := title

			if len(strings.TrimSpace(src)) > 0 {
				entity, flag, i, index := otherURL(entities, TitleStr, index2+1, configEntity)
				if index >= configEntity.StartIndexTitle {
					title = formatTitle(title, index, 4, configEntity)

					titleIndex := formatTitle2(index, 4)
					entity.URLS = append(entity.URLS, src)
					entity.TitleNames = append(entity.TitleNames, title)

					if flag {
						entities[i] = entity
					} else {
						entity.TitleStr = TitleStr
						entity.TitleIndex = titleIndex
						entity.TitleStrFormat = titleIndex + "_%d_" + title
						entity.Index = index
						entities = append(entities, entity)
						flag = true
					}
				}

			} else {
				log.Println(title, "--->", src)
			}
		})
	} else {
		body.Find(configEntity.PlayListTag).Each(func(i int, selection *goquery.Selection) {
			if IsNotEmpty(configEntity.PlayListTagAttr) {
				if !SwitchHtmlElement(selection, configEntity.PlayListTagAttr) {
					flag = false
					return
				}
			}

			selection.Find(configEntity.PlayListChildTag).Each(func(index1 int, node *goquery.Selection) {
				if !SwitchHtmlElement(node, configEntity.PlayListChildURLAttName) {
					return
				}
				flag = false
				var src, _ = node.Attr(configEntity.PlayListChildURLAtt)
				src = chooseURL(src, configEntity.BaseURL)

				index2++
				title := chooseTitle(configEntity, node, index2+1)
				title = strings.ReplaceAll(title, "第", "")
				title = strings.ReplaceAll(title, "集", "")
				TitleStr := title

				if len(strings.TrimSpace(src)) > 0 {
					entity, flag, i, index := otherURL(entities, TitleStr, index2+1, configEntity)
					if index >= configEntity.StartIndexTitle {
						title = formatTitle(title, index, 4, configEntity)

						titleIndex := formatTitle2(index, 4)
						entity.URLS = append(entity.URLS, src)
						entity.TitleNames = append(entity.TitleNames, title)

						if flag {
							entities[i] = entity
						} else {
							entity.TitleStr = TitleStr
							entity.TitleIndex = titleIndex
							entity.TitleStrFormat = titleIndex + "_%d_" + title
							entity.Index = index
							entities = append(entities, entity)
							flag = true
						}
					}

				} else {
					log.Println(title, "--->", src)
				}

			})
		})
	}

	return entities, flag
}

func otherURL(entities []entity.PlayListEntity, titleStr string, index1 int, config entity.DownloadEntity) (entity.PlayListEntity, bool, int, int) {
	tlIndex := index1
	re := regexp.MustCompile("[0-9]+")
	if IsTitleExtract(config) {

		titleIndexs := re.FindAllString(titleStr, -1)
		if nil == titleIndexs {
			return entity.PlayListEntity{}, false, -1, -1
		}
		titleIndexLength := len(titleIndexs)
		if 0 == titleIndexLength {
			return entity.PlayListEntity{}, false, -1, -1
		}
		tlIndex, _ = strconv.Atoi(strings.TrimSpace(titleIndexs[0]))

		for index, source := range entities {
			if source.TitleStr == titleStr {
				return source, true, index, tlIndex
			} else {
				titleIndex, _ := strconv.Atoi(strings.TrimSpace(re.FindAllString(source.TitleStr, -1)[0]))
				if titleIndex == tlIndex {
					return source, true, index, tlIndex
				}
			}
		}

		return entity.PlayListEntity{}, false, -1, tlIndex
	}
	return entity.PlayListEntity{}, false, index1, tlIndex
}

/*获取下一条数据*/
func nextURLFun(entities []entity.PlayListEntity, config entity.DownloadEntity, filePath []string, pageIndex int) entity.PlayListEntity {
	length := len(entities)
	var playListEntities []entity.PlayListEntity
	flag := true

	if length > 0 {
		for i := 0; i < length; i++ {
			en := entities[i]
			flag, filePath = SwitchFileExsts(en, filePath, config)
			if i < config.StartIndex || flag {
				continue
			}
			playListEntities = append(playListEntities, en)
		}
	}

	if len(playListEntities) == 0 {
		return entity.PlayListEntity{}
	}
	log.Println("---playList完成---", len(playListEntities))
	if len(playListEntities) > pageIndex {
		return playListEntities[pageIndex]
	}
	return entity.PlayListEntity{}
}

/*
判断文件是否存在
@parent fileName 文件名称
@parent configEntity  配置文件
*/
func SwitchFileExsts(playEntity entity.PlayListEntity, filePaths []string, config entity.DownloadEntity) (bool, []string) {
	for _, fileName := range playEntity.TitleNames {
		fp := CombinationFielPath(config.FileDirPath, fileName) + config.AudioType
		var flag bool
		flag, filePaths = ValidateFile(fp, config, filePaths)
		if flag {
			return flag, filePaths
		}
	}

	return false, filePaths
}

/*
判断文件是否存在
@parent fileName 文件名称
@parent configEntity  配置文件
*/
func SwitchFileExstsTitle(title string, filePaths []string, config entity.DownloadEntity) (bool, []string) {

	fp := CombinationFielPath(config.FileDirPath, title)
	var flag bool
	flag, filePaths = ValidateFile(fp, config, filePaths)
	if flag {
		return flag, filePaths
	}

	return false, filePaths
}

func ValidateFile(fp string, config entity.DownloadEntity, filePaths []string) (bool, []string) {
	if ExistsFile(fp, config) {

		filePaths = append(filePaths, fp)

		return true, filePaths
	}
	return false, filePaths
}

func ValidateFileSize(fp string, config entity.DownloadEntity, filePaths []string) (bool, []string) {
	return ValidateFileSize1(fp, config, filePaths, config.FileMinSize)
}

func ValidateFileSize2(fp string, config entity.DownloadEntity, fileSize int64) bool {
	var filePaths []string
	flag, _ := ValidateFileSize1(fp, config, filePaths, fileSize)
	return flag
}
func ValidateFileSize1(fp string, config entity.DownloadEntity, filePaths []string, fileSize int64) (bool, []string) {
	flag, filePaths := ValidateFile(fp, config, filePaths)
	if flag {
		size := GetFileSize(fp)
		if 0 == size {
			return false, filePaths
		}

		if size < fileSize {
			log.Println(fp, "file size :", size)
			os.Remove(fp)
			return false, filePaths
		}
		return true, filePaths
	}
	return false, filePaths
}

func GetFileSize(fp string) int64 {
	fi, error := os.Stat(fp)
	if nil != error {
		log.Println(fp, error)
		return 0
	}

	return fi.Size() / 1024 / 1024
}

// SwitchHtmlElement 判断是否有指定的格式
func SwitchHtmlElement(selection *goquery.Selection, attribute string) bool {
	if IsNotEmpty(attribute) {
		attributes := strings.Split(attribute, ";")
		index := 0
		set := mapset.NewSet()
		for _, attrStr := range attributes {
			if IsNotEmpty(attrStr) {
				index1 := strings.Index(attrStr, "=")
				length := len(attrStr)
				if length > 0 && index1 > 0 {
					attr := attrStr[0:index1]
					attrValue1 := attrStr[index1+1 : length]

					if IsNotEmpty(attr) && IsNotEmpty(attrValue1) {
						attrValue, flag := selection.Attr(attr)
						if flag && strings.Index(attrValue, attrValue1) > -1 {
							set.Add(true)
							index++
						}
					}
				} else if index1 == -1 {
					set.Add(true)
					index++
				}

			}
		}

		if len(attributes) == index {
			for set.Contains(false) {
				return false
			}
			return true
		}

	} else if IsEmpty(attribute) {
		return true
	}
	return false
}

/*格式化标题*/
func formatTitle(title string, index int, length int, configEntity entity.DownloadEntity) string {
	if IsTitleExtract(configEntity) {
		builder := strings.Builder{}
		builder.WriteString(formatTitle2(index, length))
		builder.WriteString("_")
		builder.WriteString(title)
		return builder.String()
	}
	return formatTitle2(index, length)
}

func IsTitleExtract(configEntity entity.DownloadEntity) bool {
	return "1" == configEntity.TitleExtract
}

func formatTitle2(index int, length int) string {
	builder := strings.Builder{}
	len := length - len(fmt.Sprintf("%d", index))
	for i := 0; i < len; i++ {
		builder.WriteString("0")
	}
	builder.WriteString(fmt.Sprintf("%d", index))
	return builder.String()
}

func chooseTitle(configEntity entity.DownloadEntity, node *goquery.Selection, index int) string {
	if IsTitleExtract(configEntity) {
		var title string
		if IsEmpty(configEntity.PlayListChildURLTitleAttName) {
			title = node.Text()
		} else {
			title, _ = node.Attr(configEntity.PlayListChildURLTitleAttName)
		}

		if IsNotEmpty(title) {
			title = strings.TrimSpace(title)
			title = strings.ReplaceAll(title, "\t", "")
			title = strings.ReplaceAll(title, "\n", "")
			title = strings.Split(title, "$")[0]
		}

		return title
	}

	return formatTitle("", index, 4, configEntity)

}

func chooseURL(src string, baseURL string) string {
	if strings.HasPrefix(strings.TrimSpace(src), "https://") || strings.HasPrefix(strings.TrimSpace(src), "http://") {
		return src
	}
	if !ExistStr(src, baseURL) {
		src = baseURL + src
	}

	return src
}

func sortPlayList(playList []entity.PlayListEntity) []entity.PlayListEntity {
	length := len(playList)
	if length == 0 {
		return playList
	}

	for i := 0; i < length; i++ {
		for j := 0; j < length-i-1; j++ {
			if playList[j].Index == 0 {
				playList[j].Index = convertNumOfTitle(playList[j].TitleStr)
			}

			if playList[j+1].Index == 0 {
				playList[j+1].Index = convertNumOfTitle(playList[j+1].TitleStr)
			}
			if playList[j].Index > playList[j+1].Index {
				temp := playList[j]
				playList[j] = playList[j+1]
				playList[j+1] = temp
			}
		}
	}
	return playList
}

func convertNumOfTitle(title string) int {
	re := regexp.MustCompile("[0-9]+")
	strs := re.FindAllString(title, -1)

	builder := strings.Builder{}
	for _, val := range strs {
		builder.WriteString(val)
	}
	if len(builder.String()) == 0 {
		return 10000000
	}
	str := strings.ReplaceAll(builder.String(), " ", "")

	value, error := strconv.Atoi(str)
	if nil != error {
		log.Println("ERROR-->convert error", title)
		return 10000000
	}
	return value
}
